package rexsee.cartoon;

import java.io.File;
import java.io.StringReader;
import java.util.HashMap;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;

import rexsee.core.browser.Browser;
import rexsee.core.browser.clazz.LogListeners;
import rexsee.core.browser.clazz.LogListeners.LogListener;
import rexsee.core.utilities.Json;
import rexsee.core.utilities.Utilities;
import rexsee.xml.XmlElement;
import android.content.Context;
import android.net.Uri;
import android.os.Environment;
import android.widget.Toast;

public class Parser {

	public final Context context;
	public final Browser browser;
	public final LogListener logListener;

	public int canvasWidth = 320;
	public int canvasHeight = 480;
	public int interval = 25;
	public String cacheDir = "file://" + Environment.getExternalStorageDirectory().getAbsolutePath();
	public boolean touchEvent = false;
	public boolean touchMoveEvent = false;

	public CartoonViewTouchListener touchListener = null;

	public final Actions actions;
	public final Cartoons cartoons;

	public Parser(Browser browser, LogListener logListener) {

		this.context = browser.getContext();
		this.browser = browser;
		this.logListener = logListener;
		actions = new Actions(this);
		cartoons = new Cartoons(this);

	}

	private boolean processRootAttributes(HashMap<String, String> attr) {

		if (attr == null || attr.size() == 0) return false;

		cacheDir = (attr.get(TagName.ATTR_ID) == null) ? cacheDir + "/cartoon" : cacheDir + "/cartoon/" + attr.get(TagName.ATTR_ID);
		File file = new File(Uri.parse(cacheDir).getPath());
		if (!file.exists() && !file.mkdirs()) {
			error("Create cache dir failed!");
			return false;
		} else if (!file.isDirectory() && (!file.delete() || !file.mkdirs())) {
			error("Rereate cache dir failed!");
			return false;
		}

		String size = attr.get(TagName.ATTR_SIZE);
		if (size != null) {
			String[] c = size.trim().toLowerCase().split("x");
			canvasWidth = Utilities.getInt(c[0], canvasWidth);
			canvasHeight = (c.length == 1) ? canvasWidth : Utilities.getInt(c[1], canvasHeight);
		}

		interval = Utilities.getInt(attr.get(TagName.ATTR_ROOT_INTERVAL), interval);
		touchEvent = "true".equalsIgnoreCase(attr.get(TagName.ATTR_ROOT_TOUCHEVENT));
		touchMoveEvent = "true".equalsIgnoreCase(attr.get(TagName.ATTR_ROOT_TOUCHMOVEEVENT));
		touchListener = new CartoonViewTouchListener(browser, touchEvent, touchMoveEvent, canvasWidth, canvasHeight);

		return true;

	}

	public Parser parse(final String mXmlContent) {

		if (mXmlContent == null) {
			error("Null animation data.");
			return null;
		}

		try {

			XmlPullParserFactory factory;
			try {
				factory = XmlPullParserFactory.newInstance();
			} catch (XmlPullParserException e) {
				error(e.getLocalizedMessage());
				return null;
			}
			XmlPullParser xpp;
			try {
				xpp = factory.newPullParser();
			} catch (XmlPullParserException e) {
				error(e.getLocalizedMessage());
				return null;
			}
			try {
				xpp.setInput(new StringReader(mXmlContent));
			} catch (XmlPullParserException e) {
				error(e.getLocalizedMessage());
				return null;
			}
			int eventType;
			try {
				eventType = xpp.getEventType();
			} catch (XmlPullParserException e) {
				error(e.getLocalizedMessage());
				return null;
			}

			XmlElement root = new XmlElement(null, "XML", 0, "root");

			XmlElement current = root;
			while (eventType != XmlPullParser.END_DOCUMENT) {
				if (eventType == XmlPullParser.START_TAG) {
					String tagName = xpp.getName().toLowerCase();
					XmlElement e = new XmlElement(current, tagName, current.depth + 1, "tag");
					int count = xpp.getAttributeCount();
					for (int i = 0; i < count; i++) {
						String name = xpp.getAttributeName(i);
						String value = xpp.getAttributeValue(i);
						e.addAttribute(name.toLowerCase(), value);
					}
					current.addChild(e);
					current = e;
				} else if (eventType == XmlPullParser.END_TAG) {
					current = current.parent;
				} else if (eventType == XmlPullParser.TEXT) {
					String t = xpp.getText().replaceAll("\\s", "");
					if (t.length() > 0) {
						XmlElement e = new XmlElement(current, "", current.depth + 1, "text");
						e.text = t;
						current.addChild(e);
					}
				}
				try {
					eventType = xpp.nextToken();
				} catch (Exception e) {
					error(e.getLocalizedMessage());
					return null;
				}
			}

			XmlElement window = root.childs.get(0);
			if (window == null || !window.tagName.equalsIgnoreCase(TagName.TAG_ROOT)) {
				error("Root tag not found.");
				return null;
			}
			if (window.childs == null || window.childs.size() == 0) {
				error("Root element has no child.");
				return null;
			}
			if (!processRootAttributes(window.attributes)) {
				error("Root attributes error:" + Json.toJson(window.attributes));
				return null;
			}

			for (int i = 0; i < window.childs.size(); i++) {
				XmlElement child = window.childs.get(i);
				if (child == null) {
					error("Null cartoon element.");
				} else if (child.tagName.equalsIgnoreCase(TagName.TAG_RES)) {
					actions.parse(child);
				} else if (child.tagName.equalsIgnoreCase(TagName.TAG_CARTOONS)) {
					cartoons.parse(child);
				} else {
					error("Error tag name: " + child.tagName);
				}
			}

			logListener.run(context, browser, LogListeners.LOG_TYPE_MESSAGE, "Total size of cached bitmap:" + actions.bitmapSize);
			return this;

		} catch (Exception e) {

			error(e.getLocalizedMessage());
			return null;

		} catch (OutOfMemoryError oom) {

			error("Out of memory.");
			return null;

		}

	}

	public void destroy() {
		actions.destroy();
		cartoons.destroy();
	}

	public void error(String message) {
		message = "[" + this.getClass().getName() + ".parser]" + message;
		logListener.run(context, browser, LogListeners.LOG_TYPE_ERROR, message);
		Toast.makeText(browser.getContext(), message, Toast.LENGTH_SHORT).show();
		browser.progressDialog.hide();
	}

}
